/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.flexengine.view;

import com.flexengine.logger.Logger;
import com.flexengine.utils.ResourceManager;
import com.flexengine.xml.Attribute;
import com.flexengine.xml.Root;
import java.awt.BufferCapabilities;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import javax.imageio.ImageIO;

/**
 * @author Luann R. Athayde
 * @version 1.0
 * @since 1.0
 */
@Root(name = "texture")
public class Texture implements Serializable {

    public static final int DEFAULT_BUFFERS = 2;
    
    public static final int MIN_BUFFERS = 1;
    public static final int MAX_BUFFERS = 16;
    
    @Attribute
    private String                name;
    @Attribute
    private String                file;
    private Dimension             size;
    private BufferedImage         buffer[];
    private TextureBufferStrategy tStrategy;

    public Texture(String name, String file, int buffers) {
        this.name      = name;
        this.file      = file;
        this.size      = new Dimension(0, 0);
        if( buffers >= MIN_BUFFERS && buffers <= MAX_BUFFERS ) {
            this.buffer    = new BufferedImage[buffers];
        }
        else {
            this.buffer    = new BufferedImage[DEFAULT_BUFFERS];
        }
        this.tStrategy = new TextureBufferStrategy();
        try {
            loadImage(new FileInputStream(ResourceManager.getLocale(file)));
        } catch (FileNotFoundException ex) {
        }
    }
    public Texture(String name, InputStream in) {
        this.name      = name;
        this.file      = null;
        this.size      = new Dimension(0, 0);
        this.buffer    = new BufferedImage[DEFAULT_BUFFERS];
        this.tStrategy = new TextureBufferStrategy();
        loadImage(in);
    }
    public Texture(String name, String file) {
        this(name,file,DEFAULT_BUFFERS);
    }
    public Texture(String name, int buffers) {
        this(name,null,buffers);
    }
    public Texture(int buffers) {
        this("texture",null,buffers);
    }
    public Texture(String name) {
        this(name,null,DEFAULT_BUFFERS);
    }
    public Texture() {
        this("texture",null,DEFAULT_BUFFERS);
    }
    
    public void loadImage() {
        Logger.logMessage("Loading texture image...");
        try {
            loadImage(new FileInputStream(ResourceManager.getLocale(file)));
        } catch (FileNotFoundException ex) {
            Logger.logWarning(getClass().getSimpleName()+" - loadImage(): "+ex);
        }
    }
    
    private void loadImage(InputStream in)    {
        BufferedImage image = loadTextureImage(in);
        if( image != null ) {
            if( buffer.length==1 ) {
                buffer[0] = image;
            } else {
                for(int i=0;i<buffer.length;i++) {
                    buffer[i] = image.getSubimage(0, 0, image.getWidth(), image.getHeight());
                }
            }
        }
    }
    
    protected BufferedImage loadTextureImage(InputStream in)  {
        BufferedImage img = null;
        try {
            img = ImageIO.read(in);
            size.setSize(img.getWidth(null), img.getHeight(null));
        } catch(OutOfMemoryError outOfMemory) {
            // -- COMO RESOLVER ISSO???
            Logger.logError(outOfMemory.toString());
            System.exit(0);
        } catch(IOException e) {
            Logger.logWarning("Failed to load texture image - ERROR: "+e);
        }
        return img;
    }

    /**
     * 
     * @return 
     */
    public synchronized Graphics getGraphics() {
        return GraphicsJ2D.newInstance(tStrategy);
    }

    /**
     * 
     * @return 
     */
    public synchronized double getWidth()   {
        Image tmp = tStrategy.getCurrentImage();
        if( tmp != null ) {
            size.setSize(tmp.getWidth(null), tmp.getHeight(null));
        }
        return size.getWidth();
    }
    /**
     * 
     * @return 
     */
    public synchronized double getHeight()   {
        Image tmp = tStrategy.getCurrentImage();
        if( tmp != null ) {
            size.setSize(tmp.getWidth(null), tmp.getHeight(null));
        }
        return size.getHeight();
    }
    /**
     * 
     * @return 
     */
    public synchronized Dimension getSize() {
        Image tmp = tStrategy.getCurrentImage();
        if( tmp != null ) {
            size.setSize(tmp.getWidth(null), tmp.getHeight(null));
        }
        return size;
    }
    /**
     * 
     * @return 
     */
    public String getName() {
        return name;
    }
    /**
     * 
     * @param name 
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * 
     * @return 
     */
    public String getFile() {
        return file;
    }

    /**
     * 
     * @param tex 
     */
    public void setTexture(Texture tex)    {
        if( tex != null ) {
            this.name   = tex.name;
            this.file   = tex.file;
            this.size   = tex.size;
            this.buffer = tex.buffer;
        } else {
            this.name   = null;
            this.file   = null;
            this.size   = null;
            this.buffer = null;
        }
    }
    
    public int getNumBuffers() {
        return buffer.length;
    }
    
    /*
    public void rotate(double angle) {
        for(int i=0;i<buffer.length;i++) {
            BufferedImage rotate = new BufferedImage(
                    buffer[i].getHeight(),
                    buffer[i].getWidth(),
                    buffer[i].getType());  
            
            Graphics2D g2 = (Graphics2D)rotate.getGraphics();
            
            AffineTransform at= new AffineTransform();
            at.rotate(angle*Math.PI/180.0f);
            g2.setTransform(at);
            g2.translate(-buffer[i].getWidth(), -buffer[i].getHeight());
            
            g2.drawImage(buffer[i],0,0,null);
            g2.dispose();
            
            buffer[i] = rotate;
        }
    }*/
    
    /**
     * 
     */
    public void rotate90() {
        for(int i=0;i<buffer.length;i++) {
            BufferedImage rotate = new BufferedImage(
                    buffer[i].getHeight(),
                    buffer[i].getWidth(),
                    buffer[i].getType());  
            
            Graphics2D gRotate = (Graphics2D)rotate.getGraphics();
            
            AffineTransform at= new AffineTransform();
            at.rotate(90*Math.PI/180.0f);
            
            gRotate.setTransform(at);
            gRotate.translate(-buffer[i].getWidth(), -buffer[i].getHeight());
            gRotate.drawImage(buffer[i],0,0,null);
            
            gRotate.dispose();
            
            buffer[i] = rotate;
        }
    }
    
    /**
     * 
     */
    public void rotate180() {
        for(int i=0;i<buffer.length;i++) {
            BufferedImage rotate = new BufferedImage(
                    buffer[i].getHeight(),
                    buffer[i].getWidth(),
                    buffer[i].getType());  
            
            Graphics2D gRotate = (Graphics2D)rotate.getGraphics();
            
            AffineTransform at= new AffineTransform();
            at.rotate(180*Math.PI/180.0f);
            
            gRotate.setTransform(at);
            gRotate.translate(-buffer[i].getWidth(), -buffer[i].getHeight());
            gRotate.drawImage(buffer[i],0,0,null);
            
            gRotate.dispose();
            
            buffer[i] = rotate;
        }
    }
    
    public void scale(double width, double height) {
        for(int i=0;i<buffer.length;i++) {
            BufferedImage rotate = new BufferedImage(
                    (int)width,
                    (int)height,
                    buffer[i].getType());  
            
            Graphics2D g2 = (Graphics2D)rotate.getGraphics();
            
            AffineTransform at= new AffineTransform();
            at.scale(width, width);
            g2.setTransform(at);
            g2.drawImage(buffer[i],0,0,null);
            g2.dispose();
            
            buffer[i] = rotate;
        }
    }
    
    /**
     * Get a sub texture with x,y positions and w,h size...
     * @param x
     * @param y
     * @param width
     * @param height
     * @return 
     */
    public synchronized Texture getSubTexture(int x, int y, int width, int height) {
        Texture tex = new Texture();
        tex.name = this.name;
        tex.file = null;
        tex.size = new Dimension(width, height);
        tex.tStrategy = this.tStrategy;
        for(int i=0;i<buffer.length;i++) {
            buffer[i] = buffer[i].getSubimage(x, y, width, height);
        }
        return tex;
    }
    
    /**
     * 
     * @return 
     */
    @Override
    public synchronized Texture clone() {
        Texture ret = new Texture();
        ret.name = this.name;
        ret.file = this.file;
        ret.size = (Dimension) this.size.clone();
        ret.buffer = new BufferedImage[this.buffer.length];
        for(int i=0;i<buffer.length;i++) {
            if( buffer[i]!=null ) {
                int w = this.buffer[i].getWidth();
                int h = this.buffer[i].getHeight();
                ret.buffer[i] = this.buffer[i].getSubimage(0, 0, w, h);
            }
        }
        return ret;
    }
    
    protected synchronized BufferedImage getImage() {
        return tStrategy.getCurrentImage();
    }
    
    /**
     * 
     * @param file
     * @return 
     */
    public static Texture getTexture(String file) {
        return new Texture("texture", file);
    }
    /**
     * 
     * @param name
     * @param file
     * @return 
     */
    public static Texture getTexture(String name, String file) {
        return new Texture(name, file);
    }
    /**
     * 
     * @param name
     * @param file
     * @param buffers
     * @return 
     */
    public static Texture getTexture(String name, String file, int buffers) {
        return new Texture(name, file, buffers);
    }
    /**
     * 
     * @param width
     * @param height
     * @param hints
     * @return 
     */
    public synchronized Texture getScaledTexture(int width, int height)    {
        return getScaledTexture(this, width, height);
    }
    
    public synchronized Texture getScaledTextureToGameWindow() {
        return getScaledTexture(this, GameWindow.getInstance().getWidth(), GameWindow.getInstance().getHeight());
    }
    
    /**
     * @param tex
     * @param width
     * @param height
     * @return 
     */
    public synchronized static Texture getScaledTexture(Texture tex, int width, int height)    {
        Texture texture;
        try {
            texture = new Texture();
            texture.name = tex.name;
            texture.file = null;
            texture.size = new Dimension(width, height);
            texture.buffer = new BufferedImage[tex.buffer.length];
            
            BufferedImage resizedImage;
            for(int i=0;i<tex.buffer.length;i++) {
                resizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g = resizedImage.createGraphics();
                //g.setComposite(AlphaComposite.Src);
                //g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                //g.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
                //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
                g.drawImage(tex.buffer[i], 0, 0, width, height, null);
                g.dispose();
                texture.buffer[i] = resizedImage;
            }
            
        } catch(Exception e) {
            Logger.logError(Texture.class.getSimpleName()+" - getScaledTexture("+tex+","+width+","+height+"): "+e);
            texture = null;
        }
        return texture;
    }

    /**
     * 
     * @return 
     */
    @Override
    public String toString() {
        return name+"["+file+"]@"+super.toString().split("@")[1];
    }
    
    /**
     * Buffer strategy for the texture...
     */
    public class TextureBufferStrategy extends BufferStrategy {

        private int bufferIndex;
        
        public TextureBufferStrategy() {
            super();
            bufferIndex = 0;
        }

        public synchronized int getBufferIndex() {
            return bufferIndex;
        }
        public synchronized int getBackBuffer() {
            int backBuffer = bufferIndex;
            if( backBuffer < buffer.length-1 ) {
                backBuffer++;
            }
            else
                backBuffer = 0;
            return backBuffer;
        }
        
        public boolean haveImages() {
            for (BufferedImage buffer1 : buffer) {
                if (buffer1 == null) {
                    return false;
                }
            }
            return true;
        }
        
        @Override
        public synchronized BufferCapabilities getCapabilities() {
            if( buffer == null || buffer.length <= 0  || !haveImages() ) return null;
            return new BufferCapabilities(
                    buffer[bufferIndex].getCapabilities(null),
                    buffer[getBackBuffer()].getCapabilities(null),
                    BufferCapabilities.FlipContents.BACKGROUND);
        }
        @Override
        public synchronized java.awt.Graphics getDrawGraphics() {
            if( buffer.length > 0 ) {
                java.awt.Graphics g = buffer[getBackBuffer()].getGraphics();
                bufferIndex++;
                if( bufferIndex >= buffer.length )
                    bufferIndex = 0;
                return g;
            }
            return null;
        }

        protected synchronized BufferedImage getCurrentImage() {
            if( buffer.length > 0  && bufferIndex<buffer.length )
                return buffer[bufferIndex];
            return null;
        }
        
        @Override
        public synchronized boolean contentsLost() {
            Image tmp = getCurrentImage();
            return tmp == null || tmp.getGraphics() == null;
        }

        @Override
        public synchronized boolean contentsRestored() {
            Image tmp = getCurrentImage();
            return tmp != null || (tmp!= null && tmp.getGraphics() != null);
        }

        @Override
        public synchronized void show() {
            //Logger.logWarning("TextureBufferStrategy - Show does not aplyed for textures...");
            if( buffer.length > 0 ) {
                BufferedImage tmp = buffer[getBackBuffer()];
                // Laze method...
                /*
                for(int i=0;i<buffer.length;i++) {
                    buffer[i] = tmp;
                }
                */
                // Fast method...
                buffer[bufferIndex] = tmp;
            }
        }

        @Override
        public void dispose() {
            if(buffer.length>0) {
                java.awt.Graphics gTmp;
                for (BufferedImage buffer1 : buffer) {
                    gTmp = buffer1.getGraphics();
                    if( gTmp != null ) {
                        gTmp.dispose();
                    }
                }
            }
        }
        
    }

}